#include "xncan.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>

CXnCan::CXnCan()
{
	m_bOpenOK = false;	
	memset(m_aCanName, 0, 64);
	m_fdSocketCan = -1;
}

CXnCan::~CXnCan()
{
	closeDevice();
}

int CXnCan::openDevice(const char *pDeviceName, int iSpeed)
{
	int iRetVal = -999;	

	if (!pDeviceName || iSpeed < 0)
	{
		return -1;
	}
	
	struct can_ctrlmode cm;
	struct ifreq ifr;
	struct sockaddr_can addr;
	int iLen = strlen(pDeviceName);
	if (iLen <= 0 || iLen >= 63)
	{
		iRetVal = -2;
	}
	else
	{
		memset(m_aCanName, 0, 64);
		memcpy(m_aCanName, pDeviceName, iLen);
		// stop Can
		if (can_do_stop(m_aCanName) < 0)
		{
			iRetVal = -3;
		}
		else
		{
			if (can_set_bitrate(m_aCanName, iSpeed) < 0)
			{
				iRetVal = -4;
			}
			else
			{
				memset(&cm, 0, sizeof(cm));			
				cm.flags |= CAN_CTRLMODE_3_SAMPLES;				
				cm.mask |= CAN_CTRLMODE_3_SAMPLES;
				
				if (can_set_ctrlmode(m_aCanName, &cm) < 0) 
				{
					iRetVal = -5;
				}
				else
				{
                                        sleep(2);// 为了can稳定，驱动厂建议停止2S add yangjb
					if (can_do_start(m_aCanName) < 0)
					{
						iRetVal = -6;
					}
					else
					{
						// create socket can
						m_fdSocketCan = socket(PF_CAN, SOCK_RAW, CAN_RAW); /* open socket */
						if (m_fdSocketCan < 0)
						{
							iRetVal = -7;
						}
						else
						{
							if (iLen >= IFNAMSIZ - 1)
							{
								iLen = IFNAMSIZ - 1;
							}
							memcpy(ifr.ifr_name, m_aCanName, iLen);
							ifr.ifr_name[iLen] = '\0';
							ifr.ifr_ifindex = if_nametoindex(ifr.ifr_name);
							if (!ifr.ifr_ifindex) 
							{
								iRetVal = -8;
							}
							else
							{								 
								 addr.can_family = AF_CAN;
								 addr.can_ifindex = ifr.ifr_ifindex;

								 if (bind(m_fdSocketCan, (struct sockaddr *)&addr, sizeof(addr)) < 0) 
								 {
									 iRetVal = -9;
								 }
								 else
								 {
									 iRetVal = 0;
									 m_bOpenOK = true;
								 }
							}
						}
					}
				}
			}
		}
	}

	return iRetVal;
}

int CXnCan::closeDevice()
{
	if (m_fdSocketCan < 0)
	{
		return -1;
	}
	
	::close(m_fdSocketCan);
	m_fdSocketCan = -1;	
	
	return can_do_stop(m_aCanName);
}

// >= 0 - read 的字节数 (> 100 为扩展帧  < 100 为标准帧)
int CXnCan::readFrame(unsigned int& uiCanId, unsigned char* pBuf, int iTimeout)
{
	if (!m_bOpenOK)
	{
		return -1;
	}
	if (!pBuf) 
	{
		return -2;
	}
	int iRetVal = -999;	
	struct can_frame frame; 
	if (-1 == m_fdSocketCan) 
	{
		return -1;
	}
	if (iTimeout >= 0)
	{
		if (select(iTimeout) == 0)
		{
			return -3;
		}
	}
	iRetVal = read(m_fdSocketCan, &frame, sizeof(frame));
	if (iRetVal < 0)
	{
		iRetVal = -4;
	}
	else
	{
		uiCanId = frame.can_id;
		memcpy(pBuf, frame.data, frame.can_dlc);
		iRetVal = frame.can_dlc;
		if (frame.can_id & CAN_EFF_FLAG) // 是否为扩展帧
		{
			iRetVal += 100;
		}
	}

	return iRetVal;
}

// >= 0 read 的帧数
int CXnCan::readMultiFrame(unsigned char* pBuf, int iFrameNum, int iTimeout)
{
	if (!m_bOpenOK)
	{
		return -1;
	}
	if (!pBuf || iFrameNum < 0)
	{
		return -2;
	}
	long unsigned int iRetVal = -999;		
	SCanMutiFrame* pCanMutiFrame = 0;
	struct can_frame frame;
	if (-1 == m_fdSocketCan)  // struct can_frame 为16字节, Linux下只能单帧读取
	{
		return -1;
	}
	if (iTimeout >= 0)
	{
		if (select(iTimeout) == 0)
		{
			return -3;
		}
	}
	iRetVal = read(m_fdSocketCan, &(frame), sizeof(struct can_frame));	
	if (iRetVal >= sizeof(struct can_frame))
	{
		pCanMutiFrame = (SCanMutiFrame*)pBuf;
		pCanMutiFrame->can_id = frame.can_id;
		pCanMutiFrame->can_dlc = frame.can_dlc;
		memcpy(pCanMutiFrame->data, frame.data, 8);
	
		iRetVal = 1;
	}
	else
	{
		iRetVal = -4;
	}

	return iRetVal;
}

// >0   - write的字节数(Data)
int CXnCan::writeFrame(unsigned int uiCanId, unsigned char* pBuf, unsigned char uaLen)
{
	if (!m_bOpenOK)
	{
		return -1;
	}
	int iRetVal = -2;
	struct can_frame frame; 
	if (-1 == m_fdSocketCan) 
	{
		return -1;
	}
	if (uaLen >= 100)
	{
		uiCanId |= CAN_EFF_FLAG;
		uaLen -= 100;
	}
	if (uaLen > 8)
	{
		uaLen = 8;
	}
	frame.can_id = uiCanId;
	frame.can_dlc = uaLen;
	memcpy(frame.data, pBuf, frame.can_dlc);
	iRetVal = write(m_fdSocketCan, &frame, sizeof(frame));
	if (iRetVal < 0)
	{
		iRetVal = -2;
	}
	else
	{
		iRetVal = uaLen;
	}

	return iRetVal;
}

int CXnCan::select(int iTimeout)
{
	struct timeval timout;
	fd_set wset,rset,eset;
	int    ret,maxfdp1;

	if (iTimeout <= 0) return 1;
	/* setup sockets to read */
	maxfdp1 = m_fdSocketCan + 1;
	FD_ZERO(&rset);
	FD_SET (m_fdSocketCan, &rset);
	FD_ZERO(&wset);
	FD_ZERO(&eset);
	timout.tv_sec  =  iTimeout / 1000;
	timout.tv_usec = (iTimeout % 1000) * 1000;

	ret = ::select(maxfdp1, &rset ,&wset, &eset, &timout);
	if (ret == 0) 
	{
		return 0; /* timeout */
	}

	return 1;
}
